#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@NAME          : logutil.py
@TIME          : 2025/03/10 11:24:00
@AUTHOR        : chenlip
@VERSION       : 0.1.0
@DESCRIPTION   : 增强的日志处理工具类（包含Flask整合）

使用方法：
from app.common.logutil import log, log_error

# 使用日志对象
log.info("正常信息记录")

# 使用错误日志函数
try:
    # 一些可能出错的代码
    result = some_function()
except Exception as e:
    log_error("操作失败", e)
    # 或使用专门的错误类型
    log_db_error("数据库查询失败", e)

# 在Flask应用中初始化(app/__init__.py)
from app.common.logutil import init_app_logger
init_app_logger(app)
'''

import os

import sys
if sys.platform.startswith('win'):
    try:
        import colorama
        colorama.init(convert=True, strip=False)
    except ImportError:
        print("要启用彩色日志，请安装 colorama: pip install colorama")

import logging
import datetime
import logging.handlers
import traceback
import functools
import inspect

from app.conf.config import Config as conf

class CustomFormatter(logging.Formatter):
    """自定义日志格式化器，支持没有额外字段时使用默认值"""
    
    def format(self, record):
        # 如果没有custom_filename字段，添加默认值
        if not hasattr(record, 'custom_filename'):
            record.custom_filename = record.filename
        
        # 如果没有custom_lineno字段，添加默认值  
        if not hasattr(record, 'custom_lineno'):
            record.custom_lineno = record.lineno
            
        # 调用父类的format方法    
        return super().format(record)

class ColoredFormatter(logging.Formatter):
    """自定义彩色日志格式化器"""
    
    # ANSI转义序列
    COLORS = {
        'DEBUG': '\033[1;34m',     # 蓝色加粗
        'INFO': '\033[1;32m',      # 绿色加粗
        'WARNING': '\033[1;33m',   # 黄色加粗
        'ERROR': '\033[1;31m',     # 红色加粗
        'CRITICAL': '\033[41;37m', # 红底白字
        'RESET': '\033[0m'         # 重置所有属性
    }
    
    def __init__(self, fmt):
        super().__init__(fmt)
    
    def format(self, record):
        # 如果没有custom_filename字段，添加默认值
        if not hasattr(record, 'custom_filename'):
            record.custom_filename = record.filename
        
        # 如果没有custom_lineno字段，添加默认值  
        if not hasattr(record, 'custom_lineno'):
            record.custom_lineno = record.lineno

        # 获取原始格式化后的日志消息
        log_message = super().format(record)
        
        # 根据日志级别添加颜色
        levelname = record.levelname
        if levelname in self.COLORS:
            # 在日志消息前后添加颜色代码
            colored_message = f"{self.COLORS[levelname]}{log_message}{self.COLORS['RESET']}"
            return colored_message
        
        # 如果没有为该级别定义颜色，返回原始消息
        return log_message

# 保留CustomRotatingFileHandler用于日志文件轮转
class CustomRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
    """
    自定义的日志文件处理器，继承自TimedRotatingFileHandler
    增加了自动创建日志目录的功能
    """
    def __init__(self, path, filename, when='D', backupCount=7, 
                 encoding='utf-8', delay=False, interval=1):
        # 确保日志目录存在
        if not os.path.exists(path):
            os.makedirs(path, exist_ok=True)
            
        # 构建完整的文件路径
        full_path = os.path.join(path, filename)
        
        # 调用父类初始化方法
        super().__init__(
            filename=full_path,
            when=when,
            backupCount=backupCount,
            encoding=encoding,
            delay=delay,
            interval=interval
        )

# 日志配置全局缓存
_log_config = None

def get_log_config():
    """获取日志配置，封装配置获取逻辑"""
    global _log_config
    
    if _log_config is None:
        _log_config = {
            'path': conf.LOGCONF.get('logfilepath', os.path.join(conf.APPLICATION_ROOT, 'logs')),
            'prefix': conf.LOGCONF.get('logfileprefix', 'wemedia'),
            'when': conf.LOGCONF.get('when', 'D'),
            'backup_count': int(conf.LOGCONF.get('backCount', 7)),
            'format': conf.LOGCONF.get('format', '%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s'),
            'encoding': conf.LOGCONF.get('logencode', 'utf-8'),
            'is_screen': int(conf.LOGCONF.get('logisscreen', 0)),
            'level': conf.LOGCONF.get('loglevel', 'info').lower()
        }
    
    return _log_config

def setup_logger(name, log_config=None):
    """配置并返回一个日志记录器"""
    if log_config is None:
        log_config = get_log_config()
        
    # 获取或创建日志记录器
    logger = logging.getLogger(name)
    
    # 如果已经配置过，直接返回
    if logger.handlers:
        return logger
        
    # 设置日志级别
    level_relations = {
        'debug': logging.DEBUG, 
        'info': logging.INFO, 
        'warning': logging.WARNING,
        'error': logging.ERROR, 
        'critical': logging.CRITICAL
    }
    logger.setLevel(level_relations.get(log_config['level'], logging.INFO))
    
    # 创建日志格式化器
    # formatter = logging.Formatter(log_config['format'])
    formatter = CustomFormatter(log_config['format'])
    
    # 创建控制台处理器（如果配置允许）
    if log_config['is_screen'] == 1:
        console_handler = logging.StreamHandler()
        # 使用彩色格式化器而不是普通格式化器
        colored_formatter = ColoredFormatter(log_config['format'])
        console_handler.setFormatter(colored_formatter)
        logger.addHandler(console_handler)
    
    # 创建文件处理器
    dt_m = datetime.date.today().strftime('%Y-%m-%d')
    log_filename = f"{log_config['prefix']}-{dt_m}.log"
    
    file_handler = CustomRotatingFileHandler(
        path=log_config['path'],
        filename=log_filename,
        when=log_config['when'],
        backupCount=log_config['backup_count'],
        encoding=log_config['encoding']
    )
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
    
    return logger

# 优先使用Flask的日志，如果不在Flask环境中则使用自定义日志
def get_logger():
    """获取当前环境下的日志记录器"""
    try:
        from flask import current_app
        # 如果在Flask应用上下文中
        return current_app.logger
    except (ImportError, RuntimeError):
        # 不在Flask环境中或无法导入Flask
        return setup_logger('wemedia')

# 初始化全局日志实例
log = get_logger()

def _log_base(message, level="info", error=None, logger=None, stack_level=1):
    """
    基础日志记录函数 - 获取调用方信息并记录日志
    """
    # 获取调用方栈帧信息，根据stack_level确定哪一级调用者
    frame = inspect.currentframe()
    for _ in range(stack_level):
        if frame and frame.f_back:
            frame = frame.f_back
        else:
            break
    
    # 如果无法获取栈帧，使用默认值
    if frame:
        filename = os.path.basename(frame.f_code.co_filename)
        lineno = frame.f_lineno
        func_name = frame.f_code.co_name
    else:
        filename = "unknown"
        lineno = 0
        func_name = "unknown"
    
    # 获取logger
    _logger = logger or get_logger()
    log_method = getattr(_logger, level, _logger.info)
    
    # 构建额外信息
    extra = {
        'custom_filename': filename,
        'custom_lineno': lineno,
        'custom_funcName': func_name
    }
    
    # 记录日志
    if error:
        log_method(f"{message}: {str(error)}", exc_info=True, extra=extra)
    else:
        log_method(message, extra=extra)

def log_info(message, error=None, logger=None):
    """
    记录调试信息：info级别
    
    Args:
        message (str): 调试消息
        error (Exception, optional): 异常对象. 默认为 None
        logger (Logger, optional): 日志记录器. 默认为 None
    """
    _log_base(message, "info", error, logger, 2)

def log_error(message, error=None, logger=None):
    """
    统一的错误日志记录函数
    
    Args:
        message (str): 错误消息
        error (Exception, optional): 异常对象. 默认为 None
        level (str, optional): 日志级别('debug','info','warning','error','critical'). 默认为 "error"
        logger (Logger, optional): 日志记录器. 默认为 None，使用全局log
    """
    _log_base(message, "error", error, logger, 2)

def log_dto_error(message, error=None):
    """数据层错误日志"""
    log_error(f"数据库操作错误: {message}", error, "error")

def log_service_error(message, error=None):
    """服务层错误日志"""
    log_error(f"服务层错误: {message}", error, "warning")

def log_api_error(message, error=None):
    """api错误日志"""
    log_error(f"api错误: {message}", error, "warning")

def log_route_error(message, error=None):
    """请求路由处理错误日志，一般拥有路由视图"""
    log_error(f"router错误: {message}", error, "error")

def init_app_logger(app):
    """
    初始化Flask应用日志系统
    
    Args:
        app: Flask应用实例
    """
    # 获取日志配置
    log_config = get_log_config()
    
    # 清除默认处理器
    for handler in app.logger.handlers:
        app.logger.removeHandler(handler)
    
    # 设置日志级别
    level_relations = {
        'debug': logging.DEBUG, 
        'info': logging.INFO, 
        'warning': logging.WARNING,
        'error': logging.ERROR, 
        'critical': logging.CRITICAL
    }
    app.logger.setLevel(level_relations.get(log_config['level'], logging.INFO))
    
    # 创建日志格式化器
    # formatter = logging.Formatter(log_config['format'])
    formatter = CustomFormatter(log_config['format'])
    
    # 添加控制台处理器（如果配置允许）
    if log_config['is_screen'] == 1:
        console_handler = logging.StreamHandler()
        # 使用彩色格式化器而不是普通格式化器
        colored_formatter = ColoredFormatter(log_config['format'])
        console_handler.setFormatter(colored_formatter)
        app.logger.addHandler(console_handler)
    
    # 添加文件处理器
    dt_m = datetime.date.today().strftime('%Y-%m-%d')
    log_filename = f"{log_config['prefix']}-flask-{dt_m}.log"
    
    file_handler = CustomRotatingFileHandler(
        path=log_config['path'],
        filename=log_filename,
        when=log_config['when'],
        backupCount=log_config['backup_count'],
        encoding=log_config['encoding']
    )
    file_handler.setFormatter(formatter)
    app.logger.addHandler(file_handler)
    
    # 配置其他Flask相关日志器
    werkzeug_logger = logging.getLogger('werkzeug')
    werkzeug_logger.handlers = []
    werkzeug_logger.setLevel(level_relations.get(log_config['level'], logging.INFO))
    werkzeug_logger.addHandler(file_handler)
    
    # 添加SQLAlchemy日志配置(如果使用)
    sqlalchemy_logger = logging.getLogger('sqlalchemy.engine')
    sqlalchemy_logger.setLevel(logging.WARNING)  # 仅记录警告和错误
    sqlalchemy_logger.addHandler(file_handler)
    
    app.logger.info("Flask应用日志系统已初始化")
    
    # 重新获取全局日志实例，确保使用Flask日志
    global log
    log = app.logger